/**
 * server
 *
 * This program runs on the Gumstix. It listens
 * for a request to connect to the client program
 * over port 8080 utilizing sockets. Once this
 * socket connection is established it utilizes the 
 * helper functions from utility.c to communicate 
 * with the camera.  It receives pictures and then 
 * sends them to the client over the socket. It 
 * continuously sends pictures until there is an error.
 * 
 * @author Team Wire-lEttuce
 * @date 12 Apr 2011
 */

/**
 * ToDo:
 *
 * Find a way so it quits nicely if the client ends session.
 */

#include "camera.h"

int main(void)
{

  struct sockaddr_storage theirAddr; // connector's address information
  socklen_t size;
  struct sigaction sa;
  char p[INET6_ADDRSTRLEN];
  unsigned char testData[NUM_BYTES];
  unsigned char testData2[NUM_BYTES];
  // boolean to check if there has been an error with the camera
  int cameraError = FALSE;  

  int s;
  int newSock;
  system("stty -F /dev/ttyS2 ispeed 38400 cs8 cstopb -brkint -icrnl -igncr -ignbrk -inlcr -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke -parenb -ixon");

  

  // Create a socket to listen on port 8080.  
  s = createListener(PORT);

  // Handle any problems raised by createListener().
  if(s == -2)
    {
      perror("bind");
      exit(1);
    }

  else if(s == -1)
    {
      perror("socket");
    }

  // Listen to the socket, wait for incoming requests.  Allow 
  // a BACKLOG of requests to come in.
  if (listen(s, BACKLOG) == -1)
    {
      perror("listen");
      exit(1);
    }


  // Set up the child reaper, a sig-action handler.
  sa.sa_handler = sigchld_handler; 
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = SA_RESTART;
  
  if (sigaction(SIGCHLD, &sa, NULL) == -1) 
    {
      perror("sigaction");
      exit(SUCCESS);
    }

  // while forever
  while(1)
    {
      size = sizeof(theirAddr);

      // create a new socket
      newSock = accept(s, (struct sockaddr *)&theirAddr, &size);
      if (newSock == ERROR)
	{
	  perror("accept");
	  continue;
	}

      inet_ntop(theirAddr.ss_family, get_in_addr((struct sockaddr *)&theirAddr), p, sizeof(p));
      

      // Make a child process to handle the request.  
      // The server will continue to listen on s.
      if(!fork()) 
	{
	  close(s);     // child process doesn't need the listener.
	  int i;        
	  // buffer to hold values read from camera
	  int buf[1];   
	  // value returned by camera when requesting size of JPG
	  int size = 0; 
	  // character buffer to store the picture received from camera
	  char picBuf[BUF_SIZE]; 

	  // open serial port connected to camera
	  if(openPort() == ERROR)
	    {
	      perror("ERR: openPort()\n");
	      exit(ERROR);
	    }
	  
	  while(1)
	    {
	      // reset to no error
	      cameraError = FALSE;

	      // reset the camera
	      if(resetCamera() == ERROR)
		{
		  perror("ERR: resetCamera()\n");
		  cameraError == TRUE;
		}

	      // wait until we receive the init Value
	      if(waitInit() == ERROR)
		{
		  perror("ERR: waitInit()\n");
		  cameraError == TRUE;
		}
	      
	      // while there is no error
	      while(cameraError == FALSE)
		{
  
		  // take the picture
		  if(takePicture() == ERROR)
		    {
		      perror("ERR: takePicture()\n");
		      cameraError == TRUE;
		      break;
		    }
  
		  // find the size of the picture
		  if((size = readFileSize()) == ERROR)
		    {
		      perror("ERR: readFileSize()\n");
		      cameraError == TRUE;
		      break;
		    }

		  // get the picture
		  if(readPicture(picBuf, size) == ERROR)
		    {
		      perror("ERR: readPicture()\n");
		      cameraError == TRUE;
		      break;
		    }

		  // send the picture to the cleint
		  if(send(newSock, picBuf, size, 0) == -1)
		    perror("send");

		  // stop taking pictures
		  if(stopTakingPictures() == ERROR)
		    {
		      perror("ERR: stopTakingPictures()");
		      cameraError == TRUE;
		      break;
		    }

		}// while
	    } 
	  
	  close(newSock);
	  exit(SUCCESS);
        
	}// if
	  
      exit(SUCCESS);
      
    }// while 

  // parent process doesn't need the new socket created to handle request.
  close(newSock);  
  return SUCCESS;
  
}

// get_in_addr()
//   Get sockaddr, IPv4 or IPv6:
//
void *get_in_addr(struct sockaddr *sa)
{
  if (sa->sa_family == AF_INET) {
    return &(((struct sockaddr_in*)sa)->sin_addr);
  }

  return &(((struct sockaddr_in6*)sa)->sin6_addr);
}


// serv_listen()
//   Create a server endpoint of communication.
//   Adapted from: "Advance Programming in the UNIX Environment."  page 501
//   as well as "Beej's Guide to Network Programming."

int createListener(const char * name)
{
  int status;
  int s;
  int yes = 1;
 
  // Defining the fields for socket structs is challenging.  
  // The fields depend of the address, type of socket, 
  // and communication protocol.  This function uses 
  // getaddrinfo() to aid in defining the struct socket 
  // fields.  This function fills a struct of type addrinfo.
  struct addrinfo hints;
  struct addrinfo * servinfo, *p; 
  
  // Initialize the hints structure based on what little we care about
  // in terms of the socket.  The goal is to listen in on host's IP
  // address on port 22.
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;        // don't care if its IPv4 or IPv6.
  hints.ai_socktype = SOCK_STREAM;    // stream-style sockets.
  hints.ai_flags = AI_PASSIVE;        // fill in my IP automatically.

  if((status = getaddrinfo(NULL, name, &hints, &servinfo)) != 0)
    {
      return ERROR;
    }

  // Servinfo now points to a linked list of 1 or more struct
  // addrinfos.  Note that they may not all be valid.  Scan through
  // the servinfo until something makes sense.
  for(p = servinfo; p != NULL; p = p->ai_next) {
    if ((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
      perror("server: socket");
      continue;
    }
    
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
      perror("setsockopt");
      exit(SUCCESS);
    }
    
    if (bind(s, p->ai_addr, p->ai_addrlen) == -1) {
      close(s);
      perror("server: bind");
      continue;
    }
    
    break;

  }

  if (p == NULL)  {
    fprintf(stderr, "server: failed to bind\n");
    return ERROR;
  }

  // Don't need servinfo anymore
  freeaddrinfo(servinfo);

  return s;
}


// sigchld_handler()
//  This function is an interrupt-driven function which 
//  reaps dead children that are forked by the server.
void sigchld_handler(int s)
{
  while(waitpid(-1, NULL, WNOHANG) > 0);
}
